﻿<!DOCTYPE HTML>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="edge" />
    <title>CrmRestKit UnitTest</title>

    <!-- scripts -->
    <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/qunit/1.11.0/qunit.min.js"></script>

    <script type="text/javascript" src="../modules/CrmRestKit.js"></script>
    <script type="text/javascript" src="../libraries/json2.js"></script>
    <script type="text/javascript" src="../../ClientGlobalContext.js.aspx"></script>

    <!-- styles -->
    <link type="text/css" media="screen" rel="stylesheet" href="http://cdnjs.cloudflare.com/ajax/libs/qunit/1.11.0/qunit.css" />
</head>
<body>
    <h1 id="qunit-header">QUnit: CrmRestKit 2.6.1</h1>
    <h2 id="qunit-banner"></h2>
    <div id="qunit-testrunner-toolbar"></div>
    <h2 id="qunit-userAgent"></h2>
    <ol id="qunit-tests"></ol>
    <div id="qunit-fixture">
        test markup, will be hidden
    </div>
</body>
<script type="text/javascript">

    /* globals QUnit, $, CrmRestKit */

    ( function () {
        'use strict';

        /*
        * QUnit notes:
        *
        * var ctx = this; --> save the scope of the 'setup'  method for the CrmRestKit-callback fn. QUnit
        * will invoke all setup, teardown, test and asyncTest method with the same scope (for each module)
        */

        ///
        /// Error-handler for the CrmRestKit
        ///
        function onRestError( xhr, status, errorThrown ) {

            debugger;
            var msg = JSON.parse( xhr.responseText ).error.message.value;

            // force fail
            QUnit.ok( false, 'gobal failded (this is really a bad thing): ' + msg );
            QUnit.start();
        }

        ///
        /// Setup: Executed before the very first test
        ///
        QUnit.begin( function () {
            // currently no inital setup required
        } );

        ///
        /// Tear-down: Executed once all test are completed
        ///
        QUnit.done( function ( failed, passed, total, runtime ) {
            
            // retrieve all account records that wher name is equals 'Test'
            var filter = "(Name eq 'Test' or Name eq 'afterUpdate')";

            CrmRestKit.ByQuery( 'Account', ['AccountId'], filter, false ).then( function fnSuccess( data ) {

                // delete the test-data
                $.each( data.d.results, function ( index, record ) {

                    CrmRestKit.Delete( 'Account', record.AccountId, false ).fail(onRestError);

                } );
               
                // the query return up to 50 records, in case more records exist, the '__next' attr. is not undefined
                if ( data.d.__next ) {

                    // recursive call
                    CrmRestKit.ByQueryUrl( data.d.__next, false ).then( fnSuccess );
                }

            }, onRestError );
        } );


        //#region CrmRestKit.Retrieve

        ///
        /// Assert: Retrieve
        ///
        QUnit.module( 'Module: CrmRestKit.Retrieve', {
            setup: function () {
                // runs before each test
                var ctx = this;
                ctx.AccountName = 'Test';

                // arrange - create dummy record
                CrmRestKit.Create( 'Account', { Name: ctx.AccountName }, false ).then( function ( data ) {

                    ctx.accountId = data.d.AccountId;
                } );
            },
            teardown: function () {
                // runs after each test

                // delete the dummy account
                CrmRestKit.Delete( 'Account', this.accountId, false );
            }
        } );

        QUnit.asyncTest( 'Retrieve account reord (async-mode)', function () {

            var ctx = this;

            // action - load account record
            CrmRestKit.Retrieve( 'Account', this.accountId, ['Name'] ).then( function ( data ) {

                // assert
                QUnit.equal( data.d.Name, ctx.AccountName, 'Retrieve passed' );

                // continue with the other tests
                QUnit.start();

            }, onRestError );
        } );

        QUnit.asyncTest( 'Retrieve "account" with all columns (asnyc-mode)', function () {

            // action - load account without columns definition
            CrmRestKit.Retrieve( 'Account', this.accountId, [] ).then( function ( data, status, xhr ) {

                var msg = 'Empty array ([]) returns all columns';

                // assert - even throw no columns are defined, the properties are populated
                QUnit.ok( data.d.AccountId !== null, msg );
                QUnit.ok( data.d.Name !== null, msg );
                QUnit.ok( data.d.CreatedOn !== null, msg );
                QUnit.ok( data.d.CreatedBy !== null, msg );

                QUnit.start();

            }, onRestError );
        } );

        QUnit.test( 'Retrieve account record (sync-mode)', function () {

            var ctx = this,
                columns = ['Name', 'AccountId', 'CreatedBy', 'CreatedOn'];

            // action - load account record
            CrmRestKit.Retrieve( 'Account', ctx.accountId, columns, false ).then( function ( data ) {

                // assert - only the fetch columns are populated
                QUnit.equal( data.d.Name, ctx.AccountName, 'Retrieve passed' );
                QUnit.ok( data.d.CreatedBy !== null, 'CreatedBy is populated' );
                QUnit.ok( data.d.AccountId !== null, 'AccountId is populated' );
                QUnit.ok( data.d.CreatedOn !== null, 'CreatedOn is populated' );
                QUnit.ok( data.d.Revenue === undefined );

            }, onRestError );
        } );

        //#endregion

        //#region CrmRestKit.RetrieveExpand

        ///
        /// Assert: RetrieveExpand
        ///
        QUnit.module('Module: CrmRestKit.RetrieveExpand', {
            setup: function () {
                // runs before each test

                var ctx = this;
                ctx.accountSet = [];
                ctx.leadSet = [];
                ctx.AccountName = 'Test Account';
                ctx.LeadName = 'Test Lead';
                // create some test-accounts
                CrmRestKit.Create('Account', { 'Name': ctx.AccountName }, false).then(function (data) {
                    ctx.accountSet.push(data.d.AccountId);

                }, onRestError);

                // create a test-leads
                CrmRestKit.Create('Lead', { LastName: ctx.LeadName }, false).then(function (data) {
                    ctx.leadSet.push(data.d.LeadId);

                }, onRestError);

                // associate the account & lead
                CrmRestKit.Associate(this.accountSet[0], 'Account', this.leadSet[0], 'Lead', 'accountleads_association', false);
            },
            teardown: function () {
                // runs after each test

                // delete all test-accounts
                $.each(this.accountSet, function (index, id) {
                    CrmRestKit.Delete('Account', id, false);
                });

                // delete all test-leads
                $.each(this.leadSet, function (index, id) {
                    CrmRestKit.Delete('Lead', id, false);
                });
            }
        });

        QUnit.asyncTest('Retrieve Expand account reord (async-mode)', function () {

            var ctx = this,
                columns = ['Name', 'accountleads_association/FullName'],
                relName = 'accountleads_association';

            // action - load account record
            CrmRestKit.RetrieveExpand('Account', ctx.accountSet[0], columns, relName).then(function (data) {

                // assert
                QUnit.equal(data.d.Name, ctx.AccountName, 'Retrieve passed');
                QUnit.equal(data.d.accountleads_association.results.length, 1, 'Retrieve associated lead');
                QUnit.equal(data.d.accountleads_association.results[0].FullName, ctx.LeadName, 'Retrieved associated lead name');
                // continue with the other tests
                QUnit.start();

            }, onRestError);
        });

        QUnit.asyncTest('Retrieve "account" with all columns (asnyc-mode)', function () {

            var ctx = this,
                relName = 'accountleads_association';

            // action - load account without columns definition
            CrmRestKit.RetrieveExpand('Account', ctx.accountSet[0], [], relName).then(function (data, status, xhr) {

                var msg = 'Empty array ([]) returns all columns';

                // assert - even throw no columns are defined, the properties are populated
                QUnit.ok(data.d.AccountId !== null, msg);
                QUnit.ok(data.d.Name !== null, msg);
                QUnit.ok(data.d.CreatedOn !== null, msg);
                QUnit.ok(data.d.CreatedBy !== null, msg);
                QUnit.equal(data.d.accountleads_association.results.length, 1, 'Retrieve associated lead');
                QUnit.equal(data.d.accountleads_association.results[0].FullName, ctx.LeadName, 'Retrieved associated lead name');
                QUnit.start();

            }, onRestError);
        });

        QUnit.test('Retrieve Expand account record (sync-mode)', function () {

            var ctx = this,
                columns = ['Name', 'AccountId', 'CreatedBy', 'CreatedOn', 'accountleads_association/FullName'],
                relName = 'accountleads_association';

            // action - load account record
            CrmRestKit.RetrieveExpand('Account', ctx.accountSet[0], columns, relName, false).then(function (data) {

                // assert - only the fetch columns are populated
                QUnit.equal(data.d.Name, ctx.AccountName, 'Retrieve passed');
                QUnit.ok(data.d.CreatedBy !== null, 'CreatedBy is populated');
                QUnit.ok(data.d.AccountId !== null, 'AccountId is populated');
                QUnit.ok(data.d.CreatedOn !== null, 'CreatedOn is populated');
                QUnit.ok(data.d.Revenue === undefined);
                QUnit.equal(data.d.accountleads_association.results.length, 1, 'Retrieve associated lead');
                QUnit.equal(data.d.accountleads_association.results[0].FullName, ctx.LeadName, 'Retrieved associated lead name');

            }, onRestError);
        });

        //#endregion

        //#region CrmRestKit.ByQuery

        ///
        /// Assert: ByQuery
        ///
        QUnit.module( 'Module: CrmRestKit.ByQuery', {
            setup: function () {

                var ctx = this;

                ctx.phone = '42154785';
                ctx.createAccountsSet = [];

                // arrange - create two dummy records
                CrmRestKit.Create( 'Account', { Name: 'Test', Telephone3: ctx.phone }, false ).then( function ( data ) {
                    ctx.createAccountsSet.push( data.d.AccountId );
                } );

                CrmRestKit.Create( 'Account', { Name: 'Test', 'Telephone3': ctx.phone }, false ).then( function ( data ) {
                    ctx.createAccountsSet.push( data.d.AccountId );
                } );
            },
            teardown: function () {

                var ctx = this;

                $.each( ctx.createAccountsSet, function ( index, id ) {

                    CrmRestKit.Delete( 'Account', id, false ).fail( onRestError );
                } );
            }
        } );

        QUnit.test( 'Retrieve multip. account records (sync-mode)', function () {

            // Action: execute request (Telephone3 eq '32323232')
            var filter = "Telephone3 eq '" + this.phone + "'";

            CrmRestKit.ByQuery( 'Account', ['AccountId'], filter, false ).then( function ( data, status, xhr ) {

                // assert
                QUnit.equal( data.d.results.length, 2, 'Expected two' );

            }, onRestError );
        } );

        QUnit.asyncTest( 'Retieve multipe. account records (async-mode)', function () {

            // Action: execute request
            CrmRestKit.ByQuery( 'Account', ['AccountId'], "Telephone3 eq '" + this.phone + "'" ).then( function ( data, status, xhr ) {

                // assert
                QUnit.equal( data.d.results.length, 2, 'Expected two, found : ' + data.d.results.length );

                // continue with the other tests
                QUnit.start();

            }, onRestError );
        } );

        QUnit.test( 'Retrieve record with german special characters -äüöß- (sync-mode)', function () {

            var ctx = this,
                germanSpeciaChars = 'foobar_äöüß 12',
                filter = "Address1_Line1 eq '" + germanSpeciaChars + "'",
                columns = ['Name', 'Address1_Line1', 'Address1_City'],
                account = {
                    Name: 'John Doe',
                    Address1_Line1: germanSpeciaChars,
                    Address1_City: 'Hameln'
                };

            // arrange - create a create a record that contains germany special characters
            CrmRestKit.Create( 'Account', account, false ).then( function ( data ) {

                ctx.createAccountsSet.push( data.d.AccountId );

            }, onRestError );

            // action - load the just created record byQuery
            CrmRestKit.ByQuery( 'Account', columns, filter, false ).then( function ( data ) {

                // assert - found on record
                QUnit.equal( data.d.results.length, 1, 'Expected one, found:', data.d.results.length );

            }, onRestError );
        } );

        QUnit.test( 'Retrieve record with muliple condtions and german special characters (sync-mode)', function () {

            var ctx = this,
                street = 'foobar_äöüß 12',
                zipCode = '31840',
                city = 'Hessisch Oldendorf',
                houseNumber = '34',
                columns = ['Name', 'Address1_Line1', 'Address1_City'],
                filter = "(Address1_PostalCode eq '" + zipCode
                        + "' and Address1_City eq '" + city
                        + "' and Address1_Line1 eq '" + street
                        + "' and Address1_Line2 eq '" + houseNumber + "')",
                account = {
                    Name: 'John Doe',
                    Address1_Line1: street,
                    Address1_Line2: houseNumber,
                    Address1_PostalCode: zipCode,
                    Address1_City: city
                };

            // arrange - create a create a record that contains germany special characters
            CrmRestKit.Create( 'Account', account, false ).then( function ( data ) {
                // save the id for the teardown method
                ctx.createAccountsSet.push( data.d.AccountId );

            }, onRestError );

            // action - load the just created record byQuery
            CrmRestKit.ByQuery( 'Account', columns, filter, false ).then( function ( data ) {

                // assert - found on record
                QUnit.equal( data.d.results.length, 1, 'Expected one, found:', data.d.results.length );

            }, onRestError );
        } );

        QUnit.asyncTest( 'Retrieve record with german special characters -äüöß- (async-mode)', function () {

            var ctx = this,
                germanSpeciaChars = 'johnDoe-Street_äöüß 1205',
                filter = "Address1_Line1 eq '" + germanSpeciaChars + "'",
                columns = ['Name', 'Address1_Line1', 'Address1_City'],
                account = {
                    Name: 'John Doe',
                    Address1_Line1: germanSpeciaChars,
                    Address1_City: 'Hameln'
                };

            // arrange - create a create a record that contains germany special characters
            CrmRestKit.Create( 'Account', account )
                .then( function ( data ) {

                    ctx.createAccountsSet.push( data.d.AccountId );
                } )
                .then( function () {

                    // action - load the just created record byQuery
                    CrmRestKit.ByQuery( 'Account', columns, filter ).then( function ( data ) {

                        // assert - found on record
                        QUnit.equal( data.d.results.length, 1, 'Expected one, found:', data.d.results.length );
                        // continue with the other tests
                        QUnit.start();

                    }, onRestError );
                }, onRestError );
        } );

        //#endregion

        //#region CrmRestKit.ByQuery

        ///
        /// Assert: ByQuery (lazy-loading)
        ///
        QUnit.module( 'Module: CrmRestKit.ByQuery (lazy-loading)', {
            setup: function () {
                // runs before each test

                var ctx = this,
                    fnSuccess = function ( data ) { ctx.AccountCollection.push( data.d.AccountId ); };

                ctx.AccountCollection = [];

                for ( var i = 0, max = 51; i < max; i++ ) {

                    CrmRestKit.Create( 'Account', { 'Name': 'Test' }, false ).then( fnSuccess, onRestError );
                }
            },
            teardown: function () {
                // runs after each test

                $.each( this.AccountCollection, function ( index, id ) {

                    CrmRestKit.Delete( 'Account', id, false ).fail( onRestError );
                } );
            }
        } );

        QUnit.test( 'Retrieve mulitple (lazy-loading) (sync-mode)', function () {

            var counter = 0,
                filter = "Name eq 'Test'";

            // action - perform query
            CrmRestKit.ByQuery( 'Account', ['AccountId'], filter, false ).then( function fnSuccess( data ) {

                // increase the page-counter
                counter++;

                if ( data.d.__next ) {

                    CrmRestKit.ByQueryUrl( data.d.__next, false ).then( fnSuccess );
                }
            } );

            // assert
            QUnit.ok( counter > 1, "Expected more than 1 batch of reocrds but only '" + counter + "' occured. " );
        } );

        QUnit.asyncTest( 'Retrieve mulitple (lazy-loading) (async-mode)', function () {

            var counter = 0,
                filter = "(Name eq 'Test' or Name eq 'afterUpdate')";

            CrmRestKit.ByQuery( 'Account', ['AccountId'], filter ).then( function fnSuccess( data ) {

                // increase the page-counter
                counter++;

                if ( data.d.__next ) {
                    CrmRestKit.ByQueryUrl( data.d.__next ).done( fnSuccess );
                }
                else {
                    // assert - covered at least two pages (each with max of 50 records)
                    QUnit.ok( counter > 1, "Expected more than 1 batch of reocrds but only '" + counter + "' occured. " );

                    // continue with the other tests
                    QUnit.start();
                }
            } );
        } );

        //#endregion

        //#region CrmRestKit.ByQueryAll

        ///
        /// Assert: Retrieve all reocrds (50+)
        ///
        QUnit.module( 'CrmRestKit.ByQueryAll', {

            setup: function () {
                // runs before each test

                var ctx = this,
                    fnSuccess = function ( data ) { ctx.AccountCollection.push( data.d.AccountId ); };

                ctx.numberOfRecords = 55;
                ctx.AccountCollection = [];
                // create 55 dummy account record
                for ( var i = 0, max = ctx.numberOfRecords; i < max; i++ ) {

                    CrmRestKit.Create( 'Account', { 'Name': 'Test' }, false ).then( fnSuccess, onRestError );
                }
            },
            teardown: function () {
                // runs after each test

                $.each( this.AccountCollection, function ( index, id ) {

                    CrmRestKit.Delete( 'Account', id, false ).fail( onRestError );
                } );
            }

        } );

        QUnit.test( 'RetrieveAll - sync', function () {

            var filter = "Name eq 'Test'",
                ctx = this;

            CrmRestKit.ByQueryAll( 'Account', ['Name'], filter, false).then( function ( data ) {

                QUnit.ok( data.length === ctx.numberOfRecords,
                    'Expected the method to load all (' + ctx.numberOfRecords + ') but received ' + data.length );

            }, onRestError );

        } );

        QUnit.asyncTest( 'RetrieveAll - aync', function () {

            var filter = "Name eq 'Test'",
                ctx = this;

            CrmRestKit.ByQueryAll('Account', ['Name'], filter ).then( function ( data ) {
                
                QUnit.ok( data.length === ctx.numberOfRecords,
                    'Expected the method to load all (' + ctx.numberOfRecords + ') but received ' + data.length );

                // continue with the other tests
                QUnit.start();

            }, onRestError );

        } );

        QUnit.asyncTest( 'Retrieve all with progress notification', function () {

            var filter = "Name eq 'Test'",
                ctx = this,
                progressCallCount = 0,
                lastProgressCallArgs = null,
                successHandler = function ( data ) {

                    // assert - progress
                    QUnit.ok( progressCallCount == 1, 'expected callcount of "1", instead "' + progressCallCount + '"' );
                    QUnit.ok( lastProgressCallArgs.d.results.length == 50, 'Expected 50, received "' + lastProgressCallArgs.d.results.length + '"' );

                    // assert - success
                    QUnit.ok( data.length === ctx.numberOfRecords,
                        'Expected the method to load all (' + ctx.numberOfRecords + ') but received ' + data.length );

                    // continue with the other tests
                    QUnit.start();
                },
                progressHandler = function ( interimData ) {

                    progressCallCount++;
                    lastProgressCallArgs = interimData;
                };

            // load all data with done-, fail- AND progress-handler
            CrmRestKit.ByQueryAll( 'Account', ['Name'], filter )
                .then( successHandler )
                .fail( onRestError )
                .progress( progressHandler );
        } );

        //#endregion

        //#region CrmRestKit.Create

        ///
        /// Assert: Create
        ///
        QUnit.module( "Module: CrmRestKit.Create", {
            setup: function () {

                var ctx = this,
                    account = { 'Name': 'Test' },
                    fnSuccess = function ( data ) { ctx.accountSet.push( data.d.AccountId ); };

                ctx.accountSet = [];

                for ( var i = 0, max = 1; i < max; i++ ) {

                    CrmRestKit.Create( 'Account', account, false ).then( fnSuccess, onRestError );
                }
            },
            teardown: function () {

                var ctx = this;

                $.each( ctx.accountSet, function ( index, item ) {
                    CrmRestKit.Delete( 'Account', item, false );
                } );
            }
        } );

        QUnit.test( 'Create a note for an account record', function () {

            var ctx = this,
                note = {
                    ObjectId: CrmRestKit.EntityReferenceFactory( ctx.accountSet[0], 'account' ),
                    NoteText: 'note body...'
                };

            CrmRestKit.Create( 'Annotation', note, false ).then( function ( data ) {

                QUnit.ok(( data.d.AnnotationId !== undefined ), 'Create passed' );

            }, onRestError );
        } );

        QUnit.test( "Create (with lookup, optionset, money) (sync-mode)", function () {

            var ctx = this,
                account = {
                    Address1_City: 'Hessisch Oldendorf_1',
                    // string attribute
                    Name: 'Test',
                    // money  attribute (value must be a string!)
                    Revenue: CrmRestKit.MoneyValueFactory( '10' ),
                    // lookup attribute
                    ParentAccountId: CrmRestKit.EntityReferenceFactory( ctx.accountSet[0] ),
                    // option-set attribute
                    AccountCategoryCode: CrmRestKit.OptionSetValueFactory( 1 )
                };

            CrmRestKit.Create( 'Account', account, false ).then( function ( data, status, xhr ) {

                // assert - lookup & picklist-values
                QUnit.ok(( data.d.AccountId !== undefined ), 'Create passed' );
                QUnit.equal( data.d.ParentAccountId.Id, account.ParentAccountId.Id, 'Lookup-match' );
                QUnit.equal( data.d.AccountCategoryCode.Value, account.AccountCategoryCode.Value, 'Optionset-match' );

                // assert - parse the money to float
                QUnit.equal( parseFloat( data.d.Revenue.Value, 10 ), parseFloat( account.Revenue.Value, 10 ), 'Revenue-match' );

            }, onRestError );
        } );

        QUnit.asyncTest( 'Create (with lookup, optionset, money -> without factory methods!) (async-mode)', function () {

            var ctx = this,
                cols = ['AccountId', 'Address1_City', 'Name', 'Revenue', 'ParentAccountId', 'AccountCategoryCode'],
                account = {
                    Address1_City: 'Hessisch Oldendorf_2',
                    // string attribute
                    Name: 'Test',
                    // money  attribute (value must be a string!)
                    Revenue: { Value: '35' },
                    // lookup attribute
                    ParentAccountId: { Id: ctx.accountSet[0], LogicalName: 'account' },
                    // option-set attribute
                    AccountCategoryCode: { Value: 1 }
                };

            CrmRestKit.Create( 'Account', account ).then( function ( data, status, xhr ) {

                // reload the just create account record
                CrmRestKit.Retrieve( 'Account', data.d.AccountId, cols ).then( function ( loaddata, status, xhr ) {

                    // assert - lookup & picklist-values
                    QUnit.ok(( loaddata.d.AccountId !== undefined ), 'Create passed' );
                    QUnit.equal( loaddata.d.ParentAccountId.Id, account.ParentAccountId.Id, 'Lookup-match' );
                    QUnit.equal( loaddata.d.AccountCategoryCode.Value, account.AccountCategoryCode.Value, 'Optionset-match' );

                    // assert - parse the money to float
                    QUnit.equal( parseFloat( loaddata.d.Revenue.Value, 10 ), parseFloat( account.Revenue.Value, 10 ), 'Revenue-match' );

                    // continue with the other tests
                    QUnit.start();
                } );
            }, onRestError );
        } );

        //#endregion

        //#region CrmRestKit.Update

        ///
        /// Assert: Update
        ///
        QUnit.module( "Module: CrmRestKit.Update", {
            setup: function () {
                // runs before each test

                // save the scope of the 'setup'  method for the CrmRestKit-callback function
                var ctx = this;

                ctx.parent = { Name: 'Test' };
                ctx.child = { Name: 'Test', ParentAccountId: null };

                // arrange - create dummy account that represents the parent
                CrmRestKit.Create( 'Account', ctx.parent, false ).then( function ( data ) {
                    ctx.parent.AccountId = data.d.AccountId;

                    // the the lookup reference
                    ctx.child.ParentAccountId = CrmRestKit.EntityReferenceFactory( ctx.parent.AccountId );
                }, onRestError );

                // arrange - create dummy record that is used in the tests
                CrmRestKit.Create( 'Account', ctx.child, false ).then( function ( data ) {
                    ctx.child.AccountId = data.d.AccountId;
                }, onRestError );
            },
            teardown: function () {
                // runs after each test

                // delete the test-accounts
                $.each( [this.parent.AccountId, this.child.AccountId], function ( index, id ) {

                    CrmRestKit.Delete( 'Account', id, false ).fail( onRestError );
                } );
            }
        } );

        QUnit.test( 'Update Account (sync-mode)', function () {

            var ctx = this,
                cols = ['Name', 'ParentAccountId'],
                after = {
                    Name: 'afterUpdate',
                    ParentAccountId: null
                };

            // assert - precondition: the parent-account-lookup is populated
            CrmRestKit.Retrieve( 'Account', ctx.child.AccountId, cols, false ).then( function ( data, status, xhr ) {

                // assert - is populated
                QUnit.ok( data.d.ParentAccountId !== null );
                QUnit.equal( data.d.ParentAccountId.Id, ctx.parent.AccountId );

                // action - update the name and the parent-lookup
                CrmRestKit.Update( 'Account', ctx.child.AccountId, after, false ).then( function ( data, status, xhr ) {

                    // reload the updated account
                    CrmRestKit.Retrieve( 'Account', ctx.child.AccountId, cols, false ).then( function ( data ) {

                        // assert - the name has changed
                        QUnit.ok( data.d.Name === 'afterUpdate', 'name has changed' );
                        QUnit.ok( data.d.ParentAccountId.Id === null, 'lookup reference is removed' );

                    } );
                }, onRestError );

            }, onRestError );
        } );

        QUnit.asyncTest( 'Update Account (async-mode)', function () {

            var ctx = this,
                cols = ['Name', 'ParentAccountId'],
                after = {
                    Name: 'afterUpdate',
                    ParentAccountId: null
                };

            // assert - precondition: the parent-account-lookup is populated
            CrmRestKit.Retrieve( 'Account', ctx.child.AccountId, cols ).then( function ( data, status, xhr ) {

                // assert - is populated
                QUnit.ok( data.d.ParentAccountId !== null );
                QUnit.equal( data.d.ParentAccountId.Id, ctx.parent.AccountId );

                // action - update the name and the parent-lookup
                CrmRestKit.Update( 'Account', ctx.child.AccountId, after ).then( function ( data, status, xhr ) {

                    // reload the updated account
                    CrmRestKit.Retrieve( 'Account', ctx.child.AccountId, cols ).then( function ( data ) {

                        // assert - the name has changed
                        QUnit.ok( data.d.Name === 'afterUpdate', 'update passed' );
                        QUnit.ok( data.d.ParentAccountId.Id === null );

                        // continue with the other tests
                        QUnit.start();
                    } );
                }, onRestError );
            }, onRestError );
        } );

        QUnit.test( 'Update Account (force error)', function () {

            var ctx = this;

            // action: Update the account record
            CrmRestKit.Update( 'Account', ctx.accountId, { fuppes: 'afterUpdate' }, false )
                .fail( function () {
                    // assert - expected error was thrown
                    QUnit.ok( true, 'Failed as expected' );
                } )
                .then( function ( data, status, xhr ) {
                    // assert - expected error what not thrown
                    QUnit.ok( false, 'Expected error' );
                } );
        } );

        //#endregion

        //#region CrmRestKit.delete

        ///
        /// Assert: Delete
        ///
        QUnit.module( 'Module: CrmRestKit.Delete', {
            setup: function () {
                // runs before each test
                var ctx = this;

                // Arrange: create dummy record
                CrmRestKit.Create( 'Account', { Name: 'Test' }, false ).then( function ( data ) {
                    ctx.accountId = data.d.AccountId;
                } );
            }
        } );

        QUnit.test( 'Delete account record (sync-mode)', function () {

            var ctx = this;

            // Action: delete the just create record
            CrmRestKit.Delete( 'Account', ctx.accountId, false ).then( function () {

                CrmRestKit.Retrieve( 'Account', ctx.accountId, ['Name'], false ).fail( function ( xhr, textStatus, errorThrown ) {
                    // assert - expected a record
                    QUnit.equal( errorThrown, 'Not Found', 'Falied as expected -> record not found' );
                } )
                .done( function ( data ) {
                    // assert - expected exception was not throwns
                    QUnit.ok( false, 'Expected exception' );
                } );
            }, onRestError );
        } );

        QUnit.asyncTest( 'Delete account record (async-mode)', function () {

            var ctx = this;

            // Action: delete the just create record
            CrmRestKit.Delete( 'Account', this.accountId ).then( function () {

                CrmRestKit.Retrieve( 'Account', ctx.accountId, ['Name'], false ).then( function ( data ) {
                    // assert
                    QUnit.ok( false, 'Expected exception' );

                    // continue with the other tests
                    QUnit.start();
                } )
            .fail( function ( xhr, textStatus, errorThrown ) {
                // assert
                QUnit.equal( errorThrown, 'Not Found', 'Falied as expected -> record not found' );

                // continue with the other tests
                QUnit.start();
            } );
            }, onRestError );
        } );

        //#endregion

        //#region CrmRestKit.Associate

        ///
        ///  Assert: Associate
        ///
        QUnit.module( 'Module: CrmRestKit.Associate', {
            setup: function () {
                // runs before each test

                var ctx = this;
                ctx.accountSet = [];
                ctx.leadSet = [];

                // create some test-accounts
                CrmRestKit.Create( 'Account', { 'Name': 'Test' }, false ).then( function ( data ) {

                    ctx.accountSet.push( data.d.AccountId );
                } );

                // create a test-leads
                CrmRestKit.Create( 'Lead', { LastName: 'Test' }, false ).then( function ( data ) {
                    ctx.leadSet.push( data.d.LeadId );

                }, onRestError );
            },
            teardown: function () {
                // runs after each test

                // delete all test-accounts
                $.each( this.accountSet, function ( index, id ) {
                    CrmRestKit.Delete( 'Account', id, false );
                } );

                // delete all test-leads
                $.each( this.leadSet, function ( index, id ) {
                    CrmRestKit.Delete( 'Lead', id, false );
                } );
            }
        } );

        QUnit.asyncTest( 'Associate account & lead (async)', function () {

            var ctx = this;

            // Action: perform associate operation
            CrmRestKit.Associate( this.accountSet[0], 'Account', this.leadSet[0], 'Lead', 'accountleads_association' ).then( function ( data ) {

                var filter = "AccountId eq guid'" + ctx.accountSet[0] + "'",
                    columns = ['Name', 'accountleads_association/FullName'],
                    relName = 'accountleads_association';

                CrmRestKit.ByExpandQuery( 'Account', columns, relName, filter, false ).then( function ( data ) {

                    var set = data.d.results[0].accountleads_association.results;

                    // Assert - n:m relationship exists
                    QUnit.equal( set.length, 1, 'Associate failed' );

                    // continue with the other tests
                    QUnit.start();

                }, onRestError );

            }, onRestError );
        } );

        //#endregion

        //#region CrmRestKit.Deassociate

        ///
        ///  Assert: Deassociate
        ///
        QUnit.module( 'Module: CrmRestKit.Disassociate', {
            setup: function () {
                // runs before each test

                var ctx = this;
                ctx.accountSet = [];
                ctx.leadSet = [];

                // create some test-accounts
                CrmRestKit.Create( 'Account', { 'Name': 'Test' }, false ).then( function ( data ) {
                    ctx.accountSet.push( data.d.AccountId );

                }, onRestError );

                // create a test-leads
                CrmRestKit.Create( 'Lead', { LastName: 'Test' }, false ).then( function ( data ) {
                    ctx.leadSet.push( data.d.LeadId );

                }, onRestError );

                // associate the account & lead
                CrmRestKit.Associate( this.accountSet[0], 'Account', this.leadSet[0], 'Lead', 'accountleads_association', false );
            },
            teardown: function () {
                // runs after each test

                // delete all test-accounts
                $.each( this.accountSet, function ( index, id ) {
                    CrmRestKit.Delete( 'Account', id, false );
                } );

                // delete all test-leads
                $.each( this.leadSet, function ( index, id ) {
                    CrmRestKit.Delete( 'Lead', id, false );
                } );
            }
        } );

        QUnit.asyncTest( 'Disassociate account & lead (async)', function () {

            var ctx = this,
                columns = ['Name', 'accountleads_association/FullName'],
                filter = "AccountId eq guid'" + ctx.accountSet[0] + "'",
                relName = 'accountleads_association',
                set = null;

            CrmRestKit.ByExpandQuery( 'Account', columns, relName, filter, false ).then( function ( data ) {

                set = data.d.results[0].accountleads_association.results;

                // Assert - n:m relationship exists
                QUnit.equal( set.length, 1, 'Associate failed' );

                // Action: perform disassociate operation
                CrmRestKit.Disassociate( ctx.accountSet[0], 'Account', ctx.leadSet[0], 'accountleads_association', false ).then( function ( data ) {

                    CrmRestKit.ByExpandQuery( 'Account', columns, relName, filter, false ).then( function ( data ) {

                        set = data.d.results[0].accountleads_association.results;

                        // Assert - n:m relationship exists
                        QUnit.equal( set.length, 0, 'Disassociate' );

                        // continue with the other tests
                        QUnit.start();
                    } );
                }, onRestError );

            }, onRestError );
        } );

        //#endregion

        //#region CrmRestKit.ParseODataDate

        QUnit.module( 'CrmRestKit.ParseODataDate' );

        QUnit.test( 'Parses an OData Date', function () {

            var odataData = '/Date(1368688809000)/';

            QUnit.equal(( typeof CrmRestKit.ParseODataDate( odataData ) ), typeof new Date() );

        } );

        //#endregion

        //#region CrmRestKit error-handling
        QUnit.module( 'Error-handling' );

        QUnit.test( 'Bad-Request will trigger the fail method -sync-', function () {

            // arrange - the web-service will through an error because the id is not valid
            var notExistingId = '0815';
            
            // action - try to update an not existing attribute (webserivce should through an error)
            CrmRestKit.Update( 'Account', notExistingId, { NotExistingAttribute: 'Test' }, false )
                .then( function ( data ) {
                    // assert 
                    QUnit.ok( false, 'The then-handler should not be called' );
                } )
                .fail( function () {
                    // assert
                    QUnit.ok( true, 'Fail-hanlder is invoked as expected ' );
                } );
        } );

        QUnit.asyncTest( 'Bad-Request will trigger the fail method -async-', function () {

            // arrange - the web-service will through an error because the id is not valid
            var notExistingId = '0815';

            // action - try to update an not existing attribute (webserivce should through an error)
            CrmRestKit.Update( 'Account', notExistingId, { NotExistingAttribute: 'Test' }, true )
                .then( function ( data ) {
                    // assert 
                    QUnit.ok( false, 'The then-handler should not be called' );
                    // continue with the other tests
                    QUnit.start();
                } )
                .fail( function () {
                    // assert
                    QUnit.ok( true, 'Fail-hanlder is invoked as expected ' );
                    // continue with the other tests
                    QUnit.start();
                } );
        } );

        //#endregion

    }() );

</script>
</html>